%%%-------------------------------------------------------------------
%%% @author Rain
%%% @copyright (C) 2022, <COMPANY>
%%% @doc
%%%
%%% @end
%%% Created : 01. 4月 2022 16:56
%%%-------------------------------------------------------------------
-module(bag).
-author("Rain").

-include("bag.hrl").

%% API
-export([add_items/3,
    del_items/3,
    sort_bag/2]).


get_data(Player) ->
    maps:get(?MODULE, Player, get_data(init, Player)).
get_data(init, _Player) ->
    #bag{}.

set_data(Bag, Player) ->
    maps:put(?MODULE, Bag, Player).



-spec add_items(AddItems, BagType, Player) -> Player1
    when
    Player :: #{atom() => term()},
    BagType :: integer(),
    AddItems :: cfg_items(),
    Player1 :: #{atom() => term()}.
%% @doc 考虑道具放入背包时需要进行道具堆叠，需要优先找到能够进行堆叠的道具位置进行优先堆叠
add_items(AddItems, BagType, Player) ->
    Bag = #bag{bag_grids = BagGrids, max_id = MaxId} = get_data(Player),
    case lists:member(BagType, ?BAG_TYPE_LIST) of
        true ->
            {TypeBagGrids, LeftBagGrids} = get_bag_type_and_left(BagType, BagGrids),
            {TypeBagGrids1, MaxId1, Player1} = add_items2(AddItems, BagType, TypeBagGrids, MaxId, Player),
            BagGrids1 = TypeBagGrids1 ++ LeftBagGrids,
            Bag1 = Bag#bag{bag_grids = BagGrids1, max_id = MaxId1},
            set_data(Bag1, Player1);
        _ ->
            throw("Bag Not Exsists")
    end.


add_items2([], _BagType, BagGrids, MaxId, Player) ->
    {BagGrids, MaxId, Player};
add_items2([AddItem | T], BagType, BagGrids, MaxId, Player) ->
    case trans_item(AddItem) of
        {ItemId, AddNum, Bind} when AddNum > 0 ->
            {BagGrids1, MaxId1, Player1} = do_add_item(BagGrids, BagType, ItemId, AddNum, Bind, [], MaxId, Player),
            add_items2(T, BagType, BagGrids1, MaxId1, Player1);
        _ ->
            add_items2(T, BagType, BagGrids, MaxId, Player)
    end.


do_add_item([], _BagType, _ItemId, AddNum, _Bind, Acc, MaxId, Player) when AddNum =< 0 ->
    {Acc, MaxId, Player};
do_add_item([], BagType, ItemId, AddNum, Bind, Acc, MaxId, Player) ->
    {Acc1, MaxId1, Player1} = create_item(BagType, ItemId, AddNum, Bind, MaxId, Acc, Player),
    {Acc1, MaxId1, Player1};

do_add_item([#item{item_id = ItemId, num = Num, bind = Bind, bag_type = BagType} = Item | T], BagType, ItemId, AddNum, Bind, Acc, MaxId, Player) when AddNum > 0 ->
    case is_item_max_num(ItemId, Num) of
        true ->
            do_add_item(T, BagType, ItemId, AddNum, Bind, [Item | Acc], MaxId, Player);
        {false, LeftNum, MaxNum} when AddNum > LeftNum ->
            do_add_item(T, BagType, ItemId, AddNum - LeftNum, Bind, [Item#item{num = MaxNum} | Acc], MaxId, Player);
        {false, _LeftNum, _MaxNum} ->
            {[Item#item{num = Num + AddNum} | Acc] ++ T, MaxId, Player}
    end;
do_add_item([Item | T], BagType, ItemId, AddNum, Bind, Acc, MaxId, Player) ->
    do_add_item(T, BagType, ItemId, AddNum, Bind, [Item | Acc], MaxId, Player).

%% @doc 创建一个新道具
create_item(BagType, ItemId, Num, Bind, MaxId, Acc, Player) ->
    MaxNum = get_item_max_num(ItemId),
    create_item2(BagType, ItemId, Bind, MaxNum, Num, MaxId, Acc, Player).

create_item2(_BagType, _ItemId, _Bind, _MaxNum, Num, MaxId, AccItems, Player) when Num =< 0 ->
    {AccItems, MaxId, Player};
create_item2(BagType, ItemId, Bind, MaxNum, Num, MaxId, AccItems, Player) ->
    ShowIndex = get_show_index(BagType, AccItems),
    SetNum = min(Num, MaxNum),
    LeftNum = max(Num - MaxNum, 0),
    AddItem = #item{item_id = ItemId, unique_id = MaxId, num = SetNum, bind = Bind, bag_type = BagType, show_index = ShowIndex},
    {AddItem1, Player1} = create_item_hook(AddItem, Player),
    create_item2(BagType, ItemId, Bind, MaxNum, LeftNum, MaxId + 1, [AddItem1 | AccItems], Player1).

%% @doc 创建道具钩子, 用于初始化不同道具的特殊属性放入item_info
create_item_hook(Item, Player) ->
    {Item, Player}.


%% @doc 道具是否为堆叠上限
%% TODO 此处模拟读表获取
is_item_max_num(ItemId, Num) ->
    MaxNum = get_item_max_num(ItemId),
    LeftNum = MaxNum - Num,
    case LeftNum =< 0 of
        true ->
            true;
        _ ->
            {false, LeftNum, MaxNum}
    end.

%% @doc 获取道具堆叠上限
%% TODO 此处模拟读表获取
get_item_max_num(1001) ->
    10;
get_item_max_num(1002) ->
    50;
get_item_max_num(1003) ->
    100;
get_item_max_num(_) ->
    200.

%% @doc 转换id为通用3element结构
trans_item({ItemId, Num}) ->
    {ItemId, Num, get_item_bind_default(ItemId)};
trans_item({ItemId, Num, Bind}) ->
    {ItemId, Num, Bind}.

%% @doc 获取道具默认绑定状态
%% TODO 此处模拟读表获取
get_item_bind_default(1001) ->
    ?ITEM_BIND_TYPE_1;
get_item_bind_default(1002) ->
    ?ITEM_BIND_TYPE_2;
get_item_bind_default(_) ->
    ?ITEM_BIND_TYPE_0.

-spec del_items(DelItems, BagType, Player) -> Player1
    when
    Player :: #{atom() => term()},
    BagType :: integer(),
    DelItems :: cfg_items(),
    Player1 :: #{atom() => term()}.
del_items(DelItems, BagType, Player) ->
    Bag = #bag{bag_grids = BagGrids} = get_data(Player),
    case lists:member(BagType, ?BAG_TYPE_LIST) of
        true ->
            {TypeBagGrids, LeftBagGrids} = get_bag_type_and_left(BagType, BagGrids),
            case del_items2(DelItems, BagType, TypeBagGrids, Player) of
                {TypeBagGrids1, Player1} ->
                    BagGrids1 = TypeBagGrids1 ++ LeftBagGrids,
                    Bag1 = Bag#bag{bag_grids = BagGrids1},
                    set_data(Bag1, Player1);
                Err ->
                    Err
            end;
        _ ->
            throw("Bag Not Exsists")
    end.

del_items2([], _BagType, BagGrids, Player) ->
    {BagGrids, Player};
del_items2([DelItem | T], BagType, BagGrids, Player) ->
    case trans_item(DelItem) of
        {ItemId, DelNum, Bind} when DelNum > 0 ->
            case do_del_item(BagGrids, BagType, ItemId, DelNum, Bind, [], Player) of
                {BagGrids1, Player1} ->
                    del_items2(T, BagType, BagGrids1, Player1);
                Err ->
                    Err
            end;
        _ ->
            del_items2(T, BagType, BagGrids, Player)
    end.


do_del_item([], _BagType, _ItemId, DelNum, _Bind, _Acc, _Player) when DelNum > 0 ->
    throw("Item not Enghou");
do_del_item([], _BagType, _ItemId, _DelNum, _Bind, Acc, Player) ->
    {Acc, Player};
do_del_item([#item{item_id = ItemId, num = Num, bind = Bind, bag_type = BagType} = Item| T], BagType, ItemId, DelNum, Bind, Acc, Player) when DelNum > 0 ->
    LeftNum = Num - DelNum,
    case LeftNum > 0 of
        true ->
            do_del_item(T, BagType, ItemId, 0, Bind, [Item#item{num = LeftNum} | Acc], Player);
        _ ->
            do_del_item(T, BagType, ItemId, -LeftNum, Bind, Acc, Player)
    end;

do_del_item([Item | T], BagType, ItemId, DelNum, Bind, Acc, Player) ->
    do_del_item(T, BagType, ItemId, DelNum, Bind, [Item | Acc], Player).

%% @doc 获取所在背包的显示位置
get_show_index(BagType, BagGrids) ->
    UsedIndexsMap = maps:from_list([{ShowIndex, 1}||#item{bag_type = AccBagType, show_index = ShowIndex}<-BagGrids, AccBagType == BagType]),
    get_show_index2(UsedIndexsMap).

get_show_index2(UsedIndexsMap) ->
    get_show_index2(1, UsedIndexsMap).

get_show_index2(NowIndex, UsedIndexsMap) ->
    case maps:get(NowIndex, UsedIndexsMap, 0) of
        0 ->
            NowIndex;
        _ ->
            get_show_index2(NowIndex + 1, UsedIndexsMap)
    end.

%% @doc 整理背包 - 更新背包显示位置
sort_bag(BagType, Player) ->
    Bag = #bag{bag_grids = BagGrids} = get_data(Player),
    {TypeBagGrids, LeftBagGrids} = get_bag_type_and_left(BagType, BagGrids),
    TypeBagGrids1 = lists:sort(fun sort_bag_fun/2, TypeBagGrids),
    Fun =
            fun(Item = #item{unique_id = UniqueId}, {AccShowIndex, AccBagGrids, AccMsg}) ->
                {AccShowIndex + 1, [Item#item{show_index = AccShowIndex}|AccBagGrids], [{UniqueId, AccShowIndex}|AccMsg]}
            end,
    {_, TypeBagGrids2, _Msg} = lists:foldl(Fun, {1, [], []}, TypeBagGrids1),
    BagGrids1 = TypeBagGrids2 ++ LeftBagGrids,
    Bag1 = Bag#bag{bag_grids = BagGrids1},
    set_data(Bag1, Player).

%% @doc 整理背包规则函数
%% 排序规则 ItemId => Bind => Num
sort_bag_fun(#item{item_id = ItemId1, bind = Bind1, num = Num1}, #item{item_id = ItemId2, bind = Bind2, num = Num2}) ->
    case ItemId1 < ItemId2 of
        true ->
            true;
        _ when ItemId1 > ItemId2 ->
            false;
        _ when Bind1 < Bind2 ->
            true;
        _ when Bind1 > Bind2 ->
            false;
        _ when Num1 < Num2 ->
            true;
        _ ->
            false
    end.


%% @doc 获取对应的BagType里的数据和剩余背包数据
%% return: {TypeBagGrids, LeftBagGrids}
get_bag_type_and_left(BagType, Player) when is_map(Player) ->
    #bag{bag_grids = BagGrids} = get_data(Player),
    get_bag_type_and_left(BagType, BagGrids);
get_bag_type_and_left(BagType, [#item{}|_] = BagGrids) ->
    Fun =
           fun(#item{bag_type = AccBagType} = Item, {Acc, AccLeft}) when AccBagType == BagType ->
                    {[Item|Acc], AccLeft};
               (Item, {Acc, AccLeft}) ->
                   {Acc, [Item|AccLeft]}
           end,
    lists:foldl(Fun, {[], []}, BagGrids);
get_bag_type_and_left(_, _) ->
    {[], []}.

